using System;
using System.Text;

using FineCollection.Intf;


namespace FineCollection.Immutable
{
    
    /// <summary>
    /// Base class for immutable collections.
    /// </summary>
    public class ImmArray<T> : RRandomList<T>, RImmutable
    {

        #region STATE

        /// <summary>
        /// Array with items. Immutable.
        /// </summary>
        /// <remarks>
        /// Note: really, the array can be filled partially - in this
        /// case the <see cref="count"/> will be not equal to the size of the array.
        /// But <c>arr.Length</c> will be not less that the <c>count</c>.
        /// </remarks>
        internal readonly T[] arr;

        /// <summary>
        /// Index of the first item.
        /// </summary>
        internal readonly int firstIndex;

        /// <summary>
        /// Index of the last item.
        /// </summary>
        internal readonly int lastIndex;

        /// <summary>
        /// Number of items in the collection.
        /// </summary>
        internal readonly int count;

        #endregion



        #region CONSTRUCTORS

        /// <summary>
        /// Trivial constructor.
        /// </summary>
        /// <param name="firstIndex">index of the first item.</param>
        /// <param name="count">how many items will be in the collection.</param>
        protected ImmArray(int firstIndex, int count)
        {
            arr = new T[count];
            this.firstIndex = firstIndex;
            this.lastIndex = firstIndex + (count - 1);
            this.count = count;
        }


        /// <summary>
        /// This constructor is for internal usage only.
        /// </summary>
        /// <param name="firstIndex">index of the first item.</param>
        /// <param name="array">array with items.</param>
        /// <param name="count">count of real itema in the array.</param>
        /// <param name="takeOwnership">true - use this array as is, 
        ///                             false - copy its content to a new array.</param>
        internal ImmArray(int firstIndex, T[] array, int count, bool takeOwnership)
        {
            this.firstIndex = firstIndex;
            this.lastIndex = firstIndex + (count - 1);
            this.count = count;

            if (takeOwnership)
            {
                this.arr = array;
            }
            else
            {   
                this.arr = new T[count];
                System.Array.Copy(array, this.arr, count);
            }
        }


        #endregion



        #region RCOLLECTION MEMBERS

        /// <summary>
        /// Number of items in the collection.
        /// </summary>
        public int Count
        {
            get { return count; }
        }

        /// <summary>
        /// Checks whether the collection is empty.
        /// </summary>
        public bool IsEmpty
        {
            get { return count == 0; }
        }

        /// <summary>
        /// checks whether the collection contains at least one item.
        /// </summary>
        public bool IsNotEmpty
        {
            get { return count > 0; }
        }


        public RIterator<T> TakeIterator()
        {
            return new ArrIterator<T>(this);
        }


        public T First
        {
            get
            {
                if (count > 0)
                    return arr[0];
                else
                    throw new CollectionIsEmpty();
            }
        }

        public T Last
        {
            get
            {
                if (count > 0)
                    return arr[count-1];
                else
                    throw new CollectionIsEmpty();
            }
        }

        public int FirstIndex
        {
            get { return firstIndex; }
        }


        public int LastIndex
        {
            get { return lastIndex; }
        }


        public T this[int index]
        {
            get
            {
                if (firstIndex <= index && index <= lastIndex)
                    return arr[index - firstIndex];
                else
                    throw new IndexOutOfBoundsException(index, firstIndex, lastIndex);
            }
        }

        #endregion



        #region LEGACY ITERATORS

        System.Collections.Generic.IEnumerator<T> System.Collections.Generic.IEnumerable<T>.GetEnumerator()
        {
            return new ArrIterator<T>(this);
        }


        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return new ArrIterator<T>(this);
        }

        #endregion



        #region ITERATOR CLASS

        internal class ArrIterator<TT> : RIterator<TT>,
                                         System.Collections.Generic.IEnumerator<TT>
        {
            internal readonly ImmArray<TT> ia;
            internal int index;
            internal bool inBounds;

            internal ArrIterator(ImmArray<TT> ia)
            {
                this.ia = ia;
                this.index = -1;
                inBounds = false;
            }

            internal ArrIterator(ImmArray<TT> ia, int index)
            {
                this.ia = ia;
                this.index = index;
                inBounds = index >= 0 && index < ia.count;
            }

            public void Reset() 
            {
                index = -1;
                inBounds = false;
            }

            public bool Next()
            {
                index++;
                inBounds = index < ia.count;
                return inBounds;
            }

            public TT Current
            {
                get
                {
                    if (inBounds)
                        return ia.arr[index];
                    else
                        throw new IndexOutOfBoundsException(ia.firstIndex + index,
                                                            ia.firstIndex,
                                                            ia.firstIndex + ia.count - 1);
                }
            }


            public sbyte Where
            {
                get
                {
                    if (index == 0)
                        return -1;
                    else if (index < ia.count)
                        return 0;
                    else
                        return +1;
                }
            }


            public RIterator<TT> Clone()
            {
                return new ArrIterator<TT>(ia, index);
            }


            bool System.Collections.IEnumerator.MoveNext()
            {
                return Next();
            }

            object System.Collections.IEnumerator.Current
            {
                get { return Current; }
            }

            void System.IDisposable.Dispose()
            {
                // do nothing
            }


            public override string ToString()
            {
                if (index < 0)
                    return "begin";
                else if (index < ia.count)
                    return (ia.firstIndex+index).ToString() + "->" + Current.ToString();
                else return "end";
            }
        }

        #endregion



        public override string ToString()
        {
            if (IsEmpty)
                return "[]";

            System.Text.StringBuilder buf = new StringBuilder(count * 8);
            bool second = false;

            for (int i = 0; i < count; i++)
            {
                if (second)
                    buf.Append(',');
                else
                    second = true;

                buf.Append(arr[i].ToString());
            }

            return buf.ToString();
        }
    }


}